from celery import shared_task, task
# from .serializers import NotificationSerializer
from .models import Notification, UserInfo, Article, Comment
from itertools import islice
from types import GeneratorType
from django.core.mail import send_mail
from celery.utils.log import get_task_logger
from django_redis import get_redis_connection
import logging

# from datetime import timedelta
# from django.utils import timezone

logger = get_task_logger('celery_fakebo')
d_logger = logging.getLogger("my_logger")

@shared_task
def create_notification(owner_id, obj_id=None, verb='', new_article_id=None,
                        new_comment_id=None, article_id=None, reply_id=None, comment_id=None, extra=None):
    # data = {
    #     "owner": owner_id,
    #     "obj": obj_id,
    #     "verb": verb,
    #     "new_article_id": new_article_id,
    #     "new_comment_id": new_comment_id,
    #     "article_id": article_id,
    #     "reply_id": reply_id,
    #     "comment_id": comment_id
    # }
    # serializer = NotificationSerializer(data=data)
    # serializer.is_valid(raise_exception=True)
    # serializer.save()
    Notification.objects.create(owner_id=owner_id, obj_id=obj_id, verb=verb, new_article_id=new_article_id,
                                new_comment_id=new_comment_id, article_id=article_id, comment_id=comment_id, extra=extra)


def _create_notifications(notifications: GeneratorType):
    if isinstance(notifications, (list, tuple)):
        notification_gen = (n for n in notifications)
    elif isinstance(notifications, GeneratorType):
        notification_gen = notifications
    else:
        return
    batch_size = 100
    while True:
        batch = list(islice(notification_gen, batch_size))
        if not batch:
            break
        Notification.objects.bulk_create(batch, batch_size)



def notify_all_followers(followee_id: int, new_article_id: int):
    d_logger.info("try to creation notifications for all followers for article creation")
    try:
        followee = UserInfo.objects.get(id=followee_id)
    except UserInfo.DoesNotExist:
        logger.warning(f'followee<{followee_id}> not found')
        return
        
    notifications = (Notification(owner=f, obj=followee, verb='issued', new_article_id=new_article_id, extra='an article')
                     for f in followee.followers.all())
    _create_notifications(notifications)
    d_logger.info(f'created notifications for {followee_id}')


def notify_the_replied(obj_id: int, new_comment_id: int, article_id: int, parent_id: int = None, parent_author_id:int=None):
    try:
        article = Article.objects.get(id=article_id)
    except Article.DoesNotExist:
        logger.warning(f'article<{article_id}> not found')
        return

    #no notification: comment one's own article
    if obj_id == article.author.id and parent_id is None:
        return

    #reply one in his article, just create reply notification, no comment notification.
    #author reply others in his article, just notify the replied, no comment notification for the author.
    if not (parent_id is not None and (parent_author_id == article.author.id or obj_id==article.author.id)):
        Notification.objects.create(owner_id=article.author.id, obj_id=obj_id, verb='commented',
                                    extra='your article', article_id=article_id, new_comment_id=new_comment_id)

    # only notify the article author:  reply one's own comment but not in one's own article
    if parent_id is not None and obj_id == parent_author_id and obj_id != article.author.id:
        return
    if not (parent_id or parent_author_id) is None :
        Notification.objects.create(owner_id=parent_author_id, obj_id=obj_id, verb='replied',
                                    extra='your comment', article_id=article_id, new_comment_id=new_comment_id, comment_id=parent_id)
        

@shared_task
def send_email(subject:str, message:str, recipient:str, from_email:str=None):
    send_mail(subject=subject, message=message, from_email=from_email, recipient_list=[recipient])


@shared_task
def sync_thumbup(model_name):
    model = eval(model_name)
    con = get_redis_connection('default')
    keys = con.keys(f'{model_name.lower()}_thumbups:*')
    objs = []
    for key in keys:
        value = ';'.join([v.decode() for v in con.smembers(key)])
        try:
            obj = model.objects.get(id=int(key.decode().split(':')[-1]))
            obj.thumbuped_by = value
            # logger.info(f"sync thumbup value:<{value}>")
            objs.append(obj)
        except model.DoesNotExist:
            continue
    batch_size = 1000
    while True:
        batch = list(islice((o for o in objs), batch_size))
        if not batch:
            break
        model.objects.bulk_update(batch, fields=['thumbuped_by'], batch_size=batch_size)
    


